package sidecar

import (
	"io"
	su "lqq/sidecar/utils"
	"net"
	"strconv"
	"time"

	CMap "github.com/orcaman/concurrent-map"
)

type serviceInst struct {
	parentService    *service
	canUse           bool
	addr             string
	reConnectTime    time.Time
	errReconnectTime int
	instID           string
	toWriteData      chan *requestMsg
	waitingReqStore  CMap.ConcurrentMap
	conn             io.ReadWriteCloser
}

func newServiceInst(parentService *service, instID string, addr string) (*serviceInst, error) {
	c, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	si := &serviceInst{
		parentService:    parentService,
		canUse:           true,
		instID:           instID,
		reConnectTime:    time.Now(),
		errReconnectTime: 0,
		addr:             addr,
		waitingReqStore:  CMap.New(),
		toWriteData:      make(chan *requestMsg, 150),
		conn:             c,
	}

	go si.writeDataThread()
	go si.readDataThread()
	return si, nil
}

func (inst *serviceInst) reConnect(imediary bool) {
	if inst.errReconnectTime > 5 {
		inst.destroy()
		return
	}
	if !imediary {
		<-time.After(2 * time.Second)
	}
	inst.reConnectTime = time.Now()
	c, err := net.Dial("tcp", inst.addr)
	if err != nil {
		inst.errReconnectTime++
		logger.error(err.Error())
		return
	}
	inst.conn = c
	inst.canUse = true
	inst.reConnectTime = time.Now()
	inst.errReconnectTime = 0
	inst.parentService.removeNotUseInst(inst)
	inst.parentService.addInst(inst)
	go inst.writeDataThread()
	go inst.readDataThread()
}

func (inst *serviceInst) destroy() {
	inst.canUse = false
	inst.waitingReqStore.Clear()
	inst.conn.Close()
	不完善  已经失效的inst还没有处理
}

func (inst *serviceInst) writeRequest(r *requestMsg) {
	inst.toWriteData <- r
}

func (inst *serviceInst) writeDataThread() {
	for {
		select {
		case r := <-inst.toWriteData:
			if !inst.canUse {
				inst.parentService.addNotUseInst(inst)
				inst.canUse = false
				inst.parentService.putRequestMsg(r)
				inst.conn.Close()
				break
			}
			b := r.toRequestBytes()
			err := su.WriteDataToConn(inst.conn, b)
			if err != nil {
				inst.parentService.addNotUseInst(inst)
				inst.canUse = false
				inst.parentService.putRequestMsg(r)
				inst.conn.Close()
				break
			}
			inst.waitingReqStore.Set(r.reqHeader.REQID, r)
		case <-time.After(10 * time.Second):
			if inst.waitingReqStore.Count() == 0 {
				inst.parentService.addNotUseInst(inst)
				inst.canUse = false
				inst.conn.Close()
				break
			}
		}
	}
}

func (inst *serviceInst) readDataThread() {
	for {
		b, err := su.ReadDataFromConn(inst.conn)
		if err != nil {
			logger.error(err.Error())
			inst.parentService.addNotUseInst(inst)
			inst.canUse = false
			inst.conn.Close()
			break
		}
		hLen := su.ComputeLen(b[:4])
		if len(b) < hLen+4 {
			continue
		}
		headBytes := b[4 : hLen+4]
		h := su.ReadPropToMap(headBytes)
		head := &RequestHeader{}
		head.REQID = h["REQID"]
		if len(head.REQID) == 0 {
			continue
		}
		r, ok := inst.waitingReqStore.Get(head.REQID)
		if !ok {
			continue
		}
		inst.waitingReqStore.Remove(head.REQID)
		re := r.(*requestMsg)
		head.TRACEID = h["TRACEID"]
		head.CLIENTID = h["CLIENTID"]
		head.ContentType = h["ContentType"]
		if len(h["StatusCode"]) > 0 {
			c, err := strconv.Atoi(h["StatusCode"])
			if err == nil {
				head.StatusCode = c
			} else {
				head.StatusCode = SUCCESSCODE
			}
		}
		head.CallURL = h["CallURL"]
		head.ServiceName = h["ServiceName"]
		re.respHeader = head
		re.respData = b[hLen+4:]
		re.semaphore <- true
	}
}
